<html>
<head><meta charset="utf-8"><title>ridiculousfish Range feedback · t-libs · Zulip Chat Archive</title></head>
<h2>Stream: <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/index.html">t-libs</a></h2>
<h3>Topic: <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html">ridiculousfish Range feedback</a></h3>

<hr>

<base href="https://rust-lang.zulipchat.com">

<head><link href="https://rust-lang.github.io/zulip_archive/style.css" rel="stylesheet"></head>

<a name="211031625"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211031625" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> pickfire <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211031625">(Sep 23 2020 at 16:58)</a>:</h4>
<p>ridiculous shared some feedback regarding <code>Range</code> as his least favorable type in rust. <a href="https://ridiculousfish.com/blog/posts/least-favorite-rust-type.html">https://ridiculousfish.com/blog/posts/least-favorite-rust-type.html</a> What do you all think?</p>
<p>Personally, I have only experience the inconvenience of <code>Range</code>when I need it to go both ways, one normal <code>Range</code> and one reversed <code>Range</code>, as they have different types I need to convert it to <code>vec</code> first which requires allocation. The end goal is to <code>for x in range { ... }</code> where <code>Range</code> can either go forward or backwards. The thinking steps go below:</p>
<ol>
<li>I would like to find a way to do <code>let range = start..=end</code> but it would not work since <code>Range</code> cannot go decremental</li>
<li>I thought of <code>let range = if start &lt;= end { start..end } else { (end..=start).rev() }</code> but it would not work since both have different types</li>
<li>Since performance is not so important yet I went with <code>let range: Vec&lt;_&gt; = if start &lt;= end { (start..=end).collect() } else { (end..=start).rev().collect() }</code> but I am still not satisfied since it requires an allocation</li>
</ol>
<p>I wonder if it is even possible to have range that really go the way it was mentioned, like <code>3..1</code> would be 3 and then 2 but meh it may cause confusion or performance issues later. In my humble opinion, having <code>3..1</code> compiled successfully (might be an issue if variables is used instead of constants) but not working for users who think it can go backwards feels like a potential foot gun to beginners.</p>



<a name="211031772"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211031772" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Joshua Nelson <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211031772">(Sep 23 2020 at 16:59)</a>:</h4>
<p>more discussion on the issue: <a href="https://github.com/rust-lang/rfcs/issues/2848">https://github.com/rust-lang/rfcs/issues/2848</a></p>



<a name="211031942"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211031942" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> pickfire <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211031942">(Sep 23 2020 at 17:00)</a>:</h4>
<p>Not just <code>Copy</code> but I outlined something different here too, interoperability between range and reversed range.</p>



<a name="211040518"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211040518" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> scottmcm <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211040518">(Sep 23 2020 at 18:04)</a>:</h4>
<p>I think the usual answer here is just <code>if go_backwards { Either::Left(r.rev()) } else { Either::Right(r) }</code>.</p>



<a name="211040679"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211040679" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> scottmcm <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211040679">(Sep 23 2020 at 18:05)</a>:</h4>
<p>This is also <a href="https://internals.rust-lang.org/t/pre-rfc-returning-automatically-generating-impl-trait/13090">https://internals.rust-lang.org/t/pre-rfc-returning-automatically-generating-impl-trait/13090</a>, in a way.</p>



<a name="211040767"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211040767" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> scottmcm <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211040767">(Sep 23 2020 at 18:06)</a>:</h4>
<p>I think it's clearly an impossible breaking change to make <code>4..=0</code> non-empty.</p>



<a name="211041105"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211041105" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> scottmcm <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211041105">(Sep 23 2020 at 18:09)</a>:</h4>
<p>I'm not a big fan of this article.  I agree with a bunch of the underlying things it's talking about, but things like</p>
<blockquote>
<p>“an out-of-bounds index is undefined behavior” but never defines what out of bounds means</p>
</blockquote>
<p>feels way overstated to me.</p>



<a name="211043711"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211043711" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> BurntSushi <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211043711">(Sep 23 2020 at 18:29)</a>:</h4>
<p>i think most of the issues brought up there are pretty minor. i don't really buy the complaining about the borrowing for example. however, the fact that Copy isn't implemented for <code>Range&lt;T&gt; where T: Copy</code> is definitely an issue i've hit and i wish we could fix it. but wouldn't want to do it at the cost of the iterator footgun.</p>



<a name="211045802"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211045802" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Steven Fackler <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211045802">(Sep 23 2020 at 18:46)</a>:</h4>
<p>I have no idea what the particulars would look like, but I think an edition boundary switching what <code>a..b</code> desugared to seems like the most plausible way forward</p>



<a name="211046441"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211046441" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Jane Lusby <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211046441">(Sep 23 2020 at 18:50)</a>:</h4>
<p><span class="user-mention silent" data-user-id="222471">BurntSushi</span> <a href="#narrow/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback/near/211043711">said</a>:</p>
<blockquote>
<p>i think most of the issues brought up there are pretty minor. i don't really buy the complaining about the borrowing for example. however, the fact that Copy isn't implemented for <code>Range&lt;T&gt; where T: Copy</code> is definitely an issue i've hit and i wish we could fix it. but wouldn't want to do it at the cost of the iterator footgun.</p>
</blockquote>
<p>have you seen <span class="user-mention" data-user-id="119009">@eddyb</span> 's suggestion on how we could fix this still? <a href="https://github.com/rust-lang/rfcs/issues/2848#issuecomment-697257283">https://github.com/rust-lang/rfcs/issues/2848#issuecomment-697257283</a></p>



<a name="211048764"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211048764" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> BurntSushi <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211048764">(Sep 23 2020 at 19:07)</a>:</h4>
<p><span class="user-mention" data-user-id="220273">@Jane Lusby</span> yes. the <code>MustClone</code> idea seems like a bit of a hack, but idk. i don't have strong opinions on how the problem is resolved. :)</p>



<a name="211054036"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211054036" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> scottmcm <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211054036">(Sep 23 2020 at 19:53)</a>:</h4>
<p>Also, I remembered this conversation about "iterator that can go forward or backwards" from 2017, where people were doing it with <code>.step_by(±1)</code>: <a href="https://github.com/rust-lang/rust/issues/27741#issuecomment-306025457">https://github.com/rust-lang/rust/issues/27741#issuecomment-306025457</a></p>



<a name="211055146"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211055146" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> scottmcm <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211055146">(Sep 23 2020 at 20:02)</a>:</h4>
<p><span class="user-mention" data-user-id="243558">@Steven Fackler</span> Agreed.</p>
<p>A sketch: we add new types, I'll call them <code>Interval</code>, that propagate <code>Copy</code> in the obvious way and are <em>not</em> <code>Iterator</code>s.  That way <code>IntervalInclusive</code> can just be two fields in the obvious way, no questions about enums or <code>is_done: bool</code>, etc.  Then those types <code>IntoIterator</code> to the existing <code>Range</code> types for iteration.  The <code>..</code> syntax produces the new types (maybe with some extra checks like <code>PartialOrd</code>), but because of the <code>IntoIterator</code> code like <code>for x in 0..10</code> continues to work.  And we implement <code>RangeBounds</code> for the new types so they continue to work in all the code that uses that.</p>
<p>That would keep the churn from being completely atrocious, but it probably still wouldn't be great, so I don't know if it's worth it.</p>



<a name="211056193"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211056193" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> scottmcm <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211056193">(Sep 23 2020 at 20:12)</a>:</h4>
<p>Another thing I've pondered, but don't really know how to fit in well: I've sometimes wished for a Range-like type with the invariant that <code>start &lt;= end</code>.  That's probably not the <code>a..b</code> type, but it has interesting properties like being able to use no-wrap flags in <code>.len()</code> and such.</p>



<a name="211056943"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211056943" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> scottmcm <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211056943">(Sep 23 2020 at 20:19)</a>:</h4>
<p><span class="user-mention silent" data-user-id="225422">pickfire</span> <a href="#narrow/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback/near/211031625">said</a>:</p>
<blockquote>
<p>In my humble opinion, having <code>3..1</code> compiled successfully (might be an issue if variables is used instead of constants) but not working for users who think it can go backwards feels like a potential foot gun to beginners.</p>
</blockquote>
<p>Well, if they're using clippy,</p>
<div class="codehilite" data-code-language="Rust"><pre><span></span><code><span class="n">error</span>: <span class="nc">this</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="n">is</span><span class="w"> </span><span class="n">empty</span><span class="w"> </span><span class="n">so</span><span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="kr">yield</span><span class="w"> </span><span class="n">no</span><span class="w"> </span><span class="n">values</span><span class="w"></span>
<span class="w"> </span><span class="o">-</span>-&gt; <span class="nc">src</span><span class="o">/</span><span class="n">main</span><span class="p">.</span><span class="n">rs</span>:<span class="mi">2</span>:<span class="mi">14</span><span class="w"></span>
<span class="w">  </span><span class="o">|</span><span class="w"></span>
<span class="mi">2</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">3</span><span class="o">..</span><span class="mi">1</span><span class="w"> </span><span class="p">{}</span><span class="w"></span>
<span class="w">  </span><span class="o">|</span><span class="w">              </span><span class="o">^^^^</span><span class="w"></span>
<span class="w">  </span><span class="o">|</span><span class="w"></span>
<span class="w">  </span><span class="o">=</span><span class="w"> </span><span class="n">note</span>: <span class="err">`</span><span class="cp">#[deny(clippy::reversed_empty_ranges)]</span><span class="err">`</span><span class="w"> </span><span class="n">on</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="n">default</span><span class="w"></span>
<span class="w">  </span><span class="o">=</span><span class="w"> </span><span class="n">help</span>: <span class="nc">for</span><span class="w"> </span><span class="n">further</span><span class="w"> </span><span class="n">information</span><span class="w"> </span><span class="n">visit</span><span class="w"> </span><span class="n">https</span>:<span class="c1">//rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges</span>
<span class="n">help</span>: <span class="nc">consider</span><span class="w"> </span><span class="n">using</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">following</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">attempting</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">iterate</span><span class="w"> </span><span class="n">over</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">reverse</span><span class="w"></span>
<span class="w">  </span><span class="o">|</span><span class="w"></span>
<span class="mi">2</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">3</span><span class="p">).</span><span class="n">rev</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"></span>
<span class="w">  </span><span class="o">|</span><span class="w">              </span><span class="o">^^^^^^^^^^^^</span><span class="w"></span>
</code></pre></div>



<a name="211058744"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211058744" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> lcnr <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211058744">(Sep 23 2020 at 20:35)</a>:</h4>
<p>(which seems like something we can and potentially should uplift into the compiler imo)</p>



<a name="211214080"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211214080" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> pickfire <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211214080">(Sep 25 2020 at 03:12)</a>:</h4>
<p><span class="user-mention" data-user-id="125270">@scottmcm</span> Nice suggestion, I almost forgot about the either type.</p>



<a name="211826042"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/219381-t-libs/topic/ridiculousfish%20Range%20feedback/near/211826042" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Philipp Korber <a href="https://rust-lang.github.io/zulip_archive/stream/219381-t-libs/topic/ridiculousfish.20Range.20feedback.html#211826042">(Sep 30 2020 at 20:56)</a>:</h4>
<p>While I don't agree with many of the points made in the article (or the context around the points) I came recently to believe that we totally over-engineered the <code>Range</code> type and that we probably shouldn't have tried to put a general purpose range type into the standard library.</p>
<p>Wrt. many aspects <code>Range</code> seems to be designed for the "ad-hoc" use case, i.e. create it using <code>..</code>/<code>..=</code> and then directly consume it.</p>
<p>Furthermore it's used in two different ways under which the start/end parameter are interpreted differently.</p>
<ul>
<li>
<p>In case of slicing it used like a POD type which bundles together the start/end of a slice, in turn if <code>end &lt; start</code> it's treated like a out-of-bounds array access.</p>
</li>
<li>
<p>In case of iteration it's treated <strong>like a iterable sequence implicitly defined by the range and the type the range is defined one</strong> and <code>end &lt;= start</code> is treated as just an empty sequence, not an error</p>
</li>
</ul>
<p>For the "ad-hoc" use case this is exactly what we want.  Having a fallible range constructor would mean instead of using <code>slice.get(start..end)</code> we would now have <code>(start..end).ok().then(|range| slice.get(range))</code> which is quite a handful. Similar<br>
panicking on <code>end &lt; start</code> is bad as this would hurt the <code>slice.get(start..end)</code> use-case.</p>
<p>But for cases where you pass a range around a lot and potentially store it in some data-structure for some algorithm this is rarely the preferred thing to have.  In such a case it's normally better to check range validity when constructing and have a <code>RangeInclusive</code> which can't be empty and as such don't need to have a additional "exhausted" flag field.</p>
<p>Not only that but using <code>Range</code> as a sequence isn't a good idea IMHO as a sequence typically consist of <code>start</code>, <code>end</code> and <code>step_size</code>. We do have the <code>Step</code> trait, but is just useful for "doing" the stepping by n steps not to define any step size or similar. It also is kinda ridiculous how complex the code behind <code>.len()</code> has become (which also has the problem that it's based on exact sized iterator len, which means it's based on <code>Iterator.size_hint()</code>, which means it's <code>usize</code> but the "len" of a <code>Range&lt;BigNum&gt;</code> can be easily bigger then <code>usize</code> (in which case you probably don't want to iterator over all of it <span aria-label="wink" class="emoji emoji-1f609" role="img" title="wink">:wink:</span> ). Which also means it uses <code>Step::steps_between</code> and has  two nested checks for validity (one of which get optimized away). But  this means that  while we do have a <code>len()</code> method on <code>Range&lt;T&gt;</code> it's not what it seems and it's broken for any <code>T</code> with more potential steps then <code>u32</code> (because on 32bit that's what <code>usize</code> is and we don't want code which accidentally only compiles on 64bit).</p>
<p>While I don't think we can change it anymore (or should try to do so) I believe it would have been better to separate <code>Range</code> and <code>iterable sequence</code> adding a bunch of (try) into conversions from range to iterable sequence and letting the type you convert it to define if <code>end &lt; start</code> is a problem in it's context or not. (Which also implies no <code>contains</code> and no <code>is_empty</code> method on <code>range</code> range would just be a way to "carry" the data from the <code>a..b</code> syntax whatever you want to use it for. ).  Furthermore types generic methods like <code>Index</code> could still accept it without conversion, doing something equivalent to calling a default conversion method internally.</p>
<p><strong>Anyway I believe the the <code>Range</code> type is a well usable compromise focused on it's most common/simple use-case.</strong></p>
<p>For everything else it might be better to opt to a library instead.</p>



<hr><p>Last updated: Aug 07 2021 at 22:04 UTC</p>
</html>